//*******************************************************************
//	created:	28:4:2011   16:02
//	filename: 	stringhelper.cpp
//	author:		tiamo
//	purpose:	string helper
//*******************************************************************

#include "stdafx.h"
#include "stringhelper.h"

namespace utils
{
	namespace
	{
		//
		// utf8 -> wchar_t
		//
		void convertChar(uint8_t const*& utf8String, uint32_t& utf8Length, UniString& retData)
		{
			uint8_t utf8Char1												= *utf8String;
			utf8String														+= 1;
			utf8Length														-= 1;

			if(utf8Char1 < 0x80)
			{
				retData.append(1, static_cast<wchar_t>(utf8Char1));
			}
			else if(utf8Char1 < 0xe0)
			{
				if(utf8Length < sizeof(uint8_t))
					ThrowException(L"invalid utf8 buffer");

				uint8_t utf8Char2											= *utf8String;
				utf8String													+= 1;
				utf8Length													-= 1;

				retData.append(1, (static_cast<wchar_t>(utf8Char1 & 0x0f) << 6) | (utf8Char2 & 0x3f));
			}
			else
			{
				if(utf8Length < sizeof(uint8_t) + sizeof(uint8_t))
					ThrowException(L"invalid utf8 buffer");

				uint8_t utf8Char2											= *utf8String;
				utf8String													+= 1;
				utf8Length													-= 1;

				uint8_t utf8Char3											= *utf8String;
				utf8String													+= 1;
				utf8Length													-= 1;

				retData.append(1, static_cast<wchar_t>(static_cast<wchar_t>(utf8Char1 & 0x0f) << 12) | (static_cast<wchar_t>(utf8Char2 & 0x3f) << 6) | static_cast<wchar_t>(utf8Char3 & 0x3f));
			}
		}

		//
		// wchar_t -> utf8
		//
		void convertChar(wchar_t const*& inputString, uint32_t& inputLength, Utf8String& retData)
		{
			wchar_t inputChar												= *inputString;
			inputString														+= 1;
			inputLength														-= 1;
			if(inputChar < 0x0080)
			{
				retData.append(1, static_cast<char>(static_cast<uint8_t>(inputChar)));
			}
			else if(inputChar < 0x0800)
			{
				retData.append(1, static_cast<char>(static_cast<uint8_t>(((inputChar >>  6) & 0x0f) | 0xc0)));
				retData.append(1, static_cast<char>(static_cast<uint8_t>(((inputChar >>  0) & 0x3f) | 0x80)));
			}
			else
			{
				retData.append(1, static_cast<char>(static_cast<uint8_t>(((inputChar >> 12) & 0x0f) | 0xe0)));
				retData.append(1, static_cast<char>(static_cast<uint8_t>(((inputChar >>  6) & 0x3f) | 0x80)));
				retData.append(1, static_cast<char>(static_cast<uint8_t>(((inputChar >>  0) & 0x3f) | 0x80)));
			}
		}
	}

	//
	// utf8 -> wchar_t
	//
	UniString utf8ToWide(char const* inputString, uint32_t inputLength)
	{
		if(inputLength == 0xffffffff)
			inputLength														= static_cast<uint32_t>(strlen(inputString));

		UniString retValue;
		uint8_t const* utf8String											= reinterpret_cast<uint8_t const*>(inputString);
		while(inputLength)
			convertChar(utf8String, inputLength, retValue);

		return retValue;
	}

	//
	// wchar_t -> utf8
	//
	Utf8String wideToUtf8(wchar_t const* inputString, uint32_t inputLength)
	{
		if(inputLength == 0xffffffff)
			inputLength														= static_cast<uint32_t>(wcslen(inputString));

		Utf8String retValue;
		while(inputLength)
			convertChar(inputString, inputLength, retValue);

		return retValue;
	}

	//
	// hex string -> binary
	//
	void hexToBinary(UniString const& inputString, uint8_t* binaryBuffer, uint32_t bufferLength)
	{
		if(bufferLength < inputString.length() / 2 || (inputString.length() % 2))
			ThrowException(L"invalid buffer length");

	#define CH_TO_INT(c)													static_cast<uint8_t>(((c)>=L'0' && (c)<=L'9') ? ((c)-L'0') : (((c)>=L'a' && (c)<=L'f') ? ((c)-L'a'+10) : (((c)>=L'A' && (c)<=L'F') ? ((c)-L'A'+10) : 0)))
		for(size_t i = 0; i < inputString.length(); i += 2)
		{
			uint8_t ch0														= CH_TO_INT(inputString[i]);
			uint8_t ch1														= CH_TO_INT(inputString[i + 1]);
			binaryBuffer[i / 2]												= static_cast<uint8_t>((ch0 << 4) + ch1);
		}
	#undef CH_TO_INT
	}

	//
	// binary -> hex string
	//
	UniString binaryToHex(uint8_t const* binaryBuffer, uint32_t bufferLength)
	{
		UniString retValue;
		retValue.reserve(bufferLength * 2);

		wchar_t temp[3]														= {0};
		for(uint32_t i = 0; i < bufferLength; i ++)
		{
			platform::snwprintf(temp, ARRAYSIZE(temp), L"%02x", binaryBuffer[i]);
			retValue.append(temp);
		}

		return retValue;
	}

	//
	// json encoding
	//
	UniString encodeJSON(UniString const& inputString)
	{
		UniString retValue;
		for(size_t i = 0; i < inputString.length(); ++ i)
		{
			wchar_t ch														= inputString[i];
			if(ch == L'"' || ch == L'\\' || ch == L'/')
			{
				retValue													+= L'\\';
				retValue													+= ch;
			}
			else if(ch == L'\b')
			{
				retValue													+= L"\\b";
			}
			else if(ch == L'\f')
			{
				retValue													+= L"\\f";
			}
			else if(ch == L'\n')
			{
				retValue													+= L"\\n";
			}
			else if(ch == L'\r')
			{
				retValue													+= L"\\r";
			}
			else if(ch == L'\t')
			{
				retValue													+= L"\\t";
			}
			else if(ch < L' ')
			{
				retValue													+= L"\\u";
				for(int j = 0; j < 4; j ++)
				{
					int value												= (ch >> 12) & 0xf;
					if (value >= 0 && value <= 9)
						retValue											+= static_cast<wchar_t>(L'0' + value);
					else if (value >= 10 && value <= 15)
						retValue											+= static_cast<wchar_t>(L'A' + (value - 10));

					ch <<= 4;
				}
			}
			else
			{
				retValue													+= ch;
			}
		}

		return retValue;
	}

	//
	// escape xml
	//
	UniString escapeXml(UniString const& xmlString)
	{
		UniString retValue;
		for(size_t i = 0; i < xmlString.length(); ++ i)
		{
			wchar_t ch														= xmlString[i];
			if(ch == L'<')
				retValue													+= L"&lt;";
			else if(ch == L'>')
				retValue													+= L"&gt;";
			else if(ch == L'&')
				retValue													+= L"&amp;";
			else if(ch == L'\r')
				retValue													+= L"&#x0d;";
			else
				retValue													+= ch;
		}

		return retValue;
	}
}
